use std::collections::HashMap;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Instant;

use anyhow::Ok;
use futures_util::TryStreamExt;
use sqlx::mysql::MySqlPoolOptions;
use sqlx::Row;
use tokio::fs::OpenOptions;
use tokio::io::{AsyncWriteExt, BufWriter};

pub mod config;

pub async fn run(url: &str, column_prefix: &str) -> anyhow::Result<()> {
    let pool = MySqlPoolOptions::new()
        .max_connections(5)
        .connect(url)
        .await?;

    let start = Instant::now();

    // 获取超级管理员ID
    let super_admin_id = {
        let sql = format!("SELECT id FROM user WHERE {column_prefix}super_admin = 1");
        let mut rows = sqlx::query(sql.as_str()).fetch(&pool);
        let row = rows.try_next().await?.unwrap();
        let id: Vec<u8> = row.get("id");
        unsafe { String::from_utf8_unchecked(id) }
    };

    // 查询用户信息
    let users = {
        let mut users = HashMap::new();
        let sql = format!("SELECT id, {column_prefix}display_name AS display_name FROM user");
        let mut rows = sqlx::query(&sql).fetch(&pool);

        while let Some(row) = rows.try_next().await.unwrap() {
            let id: Vec<u8> = row.get("id");
            let name: Vec<u8> = row.get("display_name");
            users.insert(unsafe { String::from_utf8_unchecked(id) }, unsafe {
                String::from_utf8_unchecked(name)
            });
        }
        // }

        users
    };

    log::info!("user count: {}, cost: {:?}", users.len(), start.elapsed());

    // 查询应用信息
    let apps = {
        let mut apps = vec![];
        let mut rows = sqlx::query("SELECT id FROM resource_basic").fetch(&pool);

        while let Some(row) = rows.try_next().await? {
            let id: Vec<u8> = row.get("id");
            apps.push(unsafe { String::from_utf8_unchecked(id) });
        }

        apps
    };

    log::info!("app count: {}, count: {:?}", apps.len(), start.elapsed());

    let current_time = chrono::Utc::now().timestamp_millis();

    // 创建授权
    for user in users {
        let mut apps = apps.clone();
        if super_admin_id == user.0 {
            continue;
        }

        let authorized_app_count = if apps.len() > 180 {
            rand::random_range(180..apps.len())
        } else {
            rand::random_range(0..apps.len())
        };
        let mut authorized_apps = Vec::with_capacity(authorized_app_count);
        for _ in 0..authorized_app_count {
            let index = rand::random_range(0..apps.len());
            authorized_apps.push(apps.remove(index));
        }

        let authorize_id = next_id();
        // 授权
        let resource_authorize_sql = {
            format!(
                r#"INSERT INTO `ngiam-db`.`resource_authorize` (
    `id`,
   	`{column_prefix}name`,
   	`{column_prefix}source`,
   	`{column_prefix}take_effect_date`,
   	`{column_prefix}lose_effect_date`,
   	`{column_prefix}enabled`,
   	`{column_prefix}creator_id`,
   	`{column_prefix}creator_permission_scope`,
    `{column_prefix}removed`,
    `{column_prefix}created_date`,
   	`{column_prefix}last_modified_date`,
   	`{column_prefix}tenant`
) VALUES (
    '{authorize_id}',
    '{display_name}',
    1,
    0,
    640605600000000,
    1,
    '{super_admin_id}',
    '{{"global": true, "scopes": null, "empty": false}}',
    0,
    {current_time},
    {current_time},
    'DEFAULT'
);"#,
                display_name = user.1
            )
        };
        append_large_string_async("resource_authorize.sql", resource_authorize_sql.as_str())
            .await?;
        drop(resource_authorize_sql);

        // 授权结果
        let resource_authorize_result_sql = {
            // 主体
            let subject_values = format!(
                r#"('{id}', '{authorize_id}', 1, 0, '{user_id}', 1, 0, 640605600000000, 1, 0, {current_time}, {current_time}, 'DEFAULT')"#,
                id = next_id(),
                user_id = user.0
            );

            // 客体
            let mut object_values = Vec::with_capacity(authorized_app_count);
            for app_id in &authorized_apps {
                object_values.push(format!(r#"('{id}', '{authorize_id}', 2, 1, '{app_id}', 1, 0, 640605600000000, 1, 0, {current_time}, {current_time}, 'DEFAULT')"#, id = next_id()));
            }

            format!(
                r#"INSERT INTO `resource_authorize_result` (
    `id`,
    `{column_prefix}authorize_id`,
    `{column_prefix}type`,
    `{column_prefix}target_type`,
    `{column_prefix}target_value`,
    `{column_prefix}target_count`,
    `{column_prefix}take_effect_date`,
    `{column_prefix}lose_effect_date`,
    `{column_prefix}enabled`,
    `{column_prefix}removed`,
    `{column_prefix}created_date`,
    `{column_prefix}last_modified_date`,
    `{column_prefix}tenant`
) VALUES {subject_values}, {object_values};"#,
                object_values = object_values.join(","),
            )
        };
        append_large_string_async(
            "resource_authorize_result.sql",
            resource_authorize_result_sql.as_str(),
        )
        .await?;
        drop(resource_authorize_result_sql);

        // 授权条件
        let resource_authorize_condition_sql = {
            // 主体
            let subject_values = format!(
                r#"('{id}', '{authorize_id}', 1, 1, 'USER', 'EQ', '{user_id}', 'user', 'user', 'id IN (\'{user_id}\')', 0, {current_time}, {current_time}, 'DEFAULT')"#,
                id = next_id(),
                user_id = user.0
            );

            // 客体
            let object_values = format!(
                r#"('{id}', '{authorize_id}', 2, 1, 'RESOURCE', 'IN', '{app_ids}', 'resource', 'resource_basic', 'id IN (\'{app_id_values}\')', 0, {current_time}, {current_time}, 'DEFAULT')"#,
                id = next_id(),
                app_ids = authorized_apps.join(","),
                app_id_values = authorized_apps.join("\\\',\\\'")
            );

            format!(
                r#"INSERT INTO `resource_authorize_relation_condition` (
    `id`,
    `{column_prefix}authorize_id`,
    `{column_prefix}type`,
    `{column_prefix}condition_type`,
    `{column_prefix}condition`,
    `{column_prefix}expr`,
    `{column_prefix}expr_value`,
    `{column_prefix}basis_type`,
    `{column_prefix}table_name`,
    `{column_prefix}filter_sql`,
    `{column_prefix}removed`,
    `{column_prefix}created_date`,
    `{column_prefix}last_modified_date`,
    `{column_prefix}tenant`
) VALUES {subject_values}, {object_values};"#
            )
        };
        append_large_string_async(
            "resource_authorize_relation_condition.sql",
            resource_authorize_condition_sql.as_str(),
        )
        .await?;
        drop(resource_authorize_condition_sql);

        // 账号, 账号与用户的关系
        let (account_sql, account_user_relation_sql) = {
            let mut accounts = vec![];
            let mut account_user_relations = vec![];

            for app_id in &authorized_apps {
                let account_id = next_id();
                accounts.push(format!(r#"('{id}', {current_time}, {current_time}, '{app_id}', '{user_id}', '{name}', 0, 1, 0, -27107510743000, 640605600000000, 4, 1, 0, 0, 0, {current_time}, 0, 0, {current_time}, 1, 'http', 'SHARED', {current_time}, 0, 0, 0, 0, '{user_id}', '1', 'DEFAULT')"#, id = account_id, user_id = user.0, name = user.1));

                account_user_relations.push(format!(r#"('{id}', {current_time}, {current_time}, '{app_id}', 0, '{account_id}', '{user_id}', '{user_id}', 3, 0, -27107510743000, 640605600000000, 1, 4, 'DEFAULT')"#, id = next_id(), user_id = user.0));
            }

            (
                format!(
                    r#"INSERT INTO `account` (
    `id`,
    `{column_prefix}created_date`,
    `{column_prefix}last_modified_date`,
    `{column_prefix}resource_id`,
    `{column_prefix}user_id`,
    `{column_prefix}account_name`,
    `{column_prefix}random_secret`,
    `{column_prefix}enabled`,
    `{column_prefix}removed`,
    `{column_prefix}take_effect_date`,
    `{column_prefix}lose_efficacy_date`,
    `{column_prefix}source`,
    `{column_prefix}trusteeship`,
    `{column_prefix}privilege`,
    `{column_prefix}_admin`,
    `{column_prefix}safe`,
    `{column_prefix}create_time_stamp`,
    `{column_prefix}shared`,
    `{column_prefix}entrusted`,
    `{column_prefix}password_update`,
    `{column_prefix}auto_push`,
    `{column_prefix}protocol`,
    `{column_prefix}share_way`,
    `{column_prefix}last_use_time`,
    `{column_prefix}used`,
    `{column_prefix}auto_generate_key`,
    `{column_prefix}category_type`,
    `{column_prefix}account_type`,
    `{column_prefix}mapping_attr_bind_user_id`,
    `{column_prefix}uk`,
    `{column_prefix}tenant`
) VALUES {accounts};"#,
                    accounts = accounts.join(","),
                ),
                format!(
                    r#"INSERT INTO `account_relation_user` (
    `id`,
    `{column_prefix}created_date`,
    `{column_prefix}last_modified_date`,
    `{column_prefix}resource_id`,
    `{column_prefix}resource_category_type`,
    `{column_prefix}account_id`,
    `{column_prefix}user_id`,
    `{column_prefix}entrusted_user_id`,
    `{column_prefix}relation_type`,
    `{column_prefix}removed`,
    `{column_prefix}take_effect_date`,
    `{column_prefix}lose_efficacy_date`,
    `{column_prefix}trusteeship`,
    `{column_prefix}operation_source`,
    `{column_prefix}tenant`
) VALUES {account_user_relations};"#,
                    account_user_relations = account_user_relations.join(","),
                ),
            )
        };
        append_large_string_async("account.sql", account_sql.as_str()).await?;
        drop(account_sql);
        append_large_string_async(
            "account_relation_user.sql",
            account_user_relation_sql.as_str(),
        )
        .await?;
        drop(account_user_relation_sql);
    }

    log::info!("generated successfully. cost: {:?}", start.elapsed());
    Ok(())
}

static ID: AtomicU64 = AtomicU64::new(1968499977508643841);

fn next_id() -> u64 {
    ID.fetch_add(1, Ordering::Relaxed)
}

async fn append_large_string_async(file_path: &str, content: &str) -> anyhow::Result<()> {
    let file = OpenOptions::new()
        .create(true)
        .append(true)
        .open(file_path)
        .await?;

    let mut writer = BufWriter::new(file);
    writer.write_all(content.as_bytes()).await?;
    writer.write_all(b"\n").await?;
    writer.flush().await?;
    Ok(())
}
